﻿using System;
using System.Collections.Generic;
using System.Xml.Linq;

namespace OrbitOne.Elmah.Logic
{
    /// <summary>
    /// Represents the reporting helper. This class will help you retreive specific data to create the emails necessary for the MailNotifier.
    /// </summary>
    public static class ReportingHelper
    {
        #region Private Member
        private static OrbitOneElmahErrorService service = new OrbitOneElmahErrorService();
        #endregion

        #region Public Method
        /// <summary>
        /// Generates the daily report <code>XElement</code>
        /// </summary>
        /// <param name="elmahBaseUrl">The elmah base url. This url will be used as prefix for the hyperlinks</param>
        /// <param name="after"></param>
        /// <returns>The generated report data in an <code>XElement</code>.</returns>
        /// <exception cref="ArgumentNullException">Thrown when elmahBaseUrl is null.</exception>
        public static XElement CreateDailyReportData(string elmahBaseUrl, DateTime after)
        {
            if (elmahBaseUrl == null) throw new ArgumentNullException("elmahBaseUrl");

            var topLink = elmahBaseUrl + @"/Pages/Search.aspx?app={0}&tf=1&gv=3&ut=15";
            var messLink = elmahBaseUrl + @"/Pages/ErrorDetail.aspx?id={0}";

            #region Create the Report parts.

            var appList = service.GetErrorCountGroupedByApplication(after);
            var applications = new XElement("applications");
            foreach (var kv in appList)
                applications.Add(new XElement("application",
                                              new XAttribute("name", kv.Key),
                                              new XAttribute("count", kv.Value),
                                              new XAttribute("link", string.Format(topLink, kv.Key))));

            var messList = service.GetMessageErrorCounts(after);
            var messages = new XElement("messages");
            foreach (var kv in messList)
            {
                messages.Add(new XElement("message",
                                          new XAttribute("name", (kv.Key.ErrorMessage.Length > 100 ? kv.Key.ErrorMessage.Substring(0, 100) + "..." : kv.Key.ErrorMessage)),
                                          new XAttribute("count", kv.Value),
                                          new XAttribute("link", string.Format(messLink, kv.Key.ErrorId.ToString("D"))),
                                          new XAttribute("type", kv.Key.ErrorType),
                                          new XAttribute("level", kv.Key.ErrorLevel)));
            }

            var urlList = service.GetUrlErrorCountsForMessagesGroupedByUrl(after);
            var urls = new XElement("urls");
            foreach (var kv in urlList)
            {
                urls.Add(new XElement("url",
                                      new XAttribute("name", kv.Error.ApplicationName + kv.Url),
                                      new XAttribute("count", kv.ErrorCount),
                                      new XAttribute("link", string.Format(messLink, kv.Error.ErrorId.ToString("D")))));
            }

            #endregion Create the Report parts.

            XElement retval;
            if(!applications.HasElements && !messages.HasElements && !urls.HasElements)
            {
                retval = null;
            }
            else
            {
                retval = new XElement("report",
                                      new XAttribute("name", "daily"),
                                      new XAttribute("from", after.ToString("dd/MM/yyyy HH:mm")),
                                      new XAttribute("to", DateTime.Now.ToString("dd/MM/yyyy HH:mm")),
                                      applications,
                                      messages,
                                      urls
                    );
            }
            return retval;
        }

        /// <summary>
        /// Generates the alert reports data <code>XElement</code>s
        /// </summary>
        /// <param name="elmahBaseUrl">The elmah base url. This url will be used as prefix for the hyperlinks</param>
        /// <param name="minutes">The nr of minutes you want the report to go back in time.</param>
        /// <param name="alertAnonymousErrorCount"></param>
        /// <param name="alertAuthenticatedErrorCount"></param>
        /// <returns>The generated report data in a <code>SortedList</code>, 
        /// containing report data in <code>XElement</code>s per application</returns>
        /// <exception cref="ArgumentNullException">Thrown when elmahBaseUrl is null.</exception>
        public static SortedList<string, XElement> CreateAlertReportsData(string elmahBaseUrl, int minutes, int alertAnonymousErrorCount, int alertAuthenticatedErrorCount)
        {
            if (elmahBaseUrl == null) throw new ArgumentNullException("elmahBaseUrl");

            var retval = new SortedList<string, XElement>();
            var messLink = elmahBaseUrl + @"/Pages/ErrorDetail.aspx?id={0}";

            var applications = GetApplicationsToAlert(minutes, alertAnonymousErrorCount, alertAuthenticatedErrorCount);
            foreach (var app in applications)
            {
                var tmp = new XElement("messages");
                var messList = service.GetApplicationMessageErrorCount(DateTime.Now.AddMinutes(-minutes), app);
                foreach (var kv in messList)
                {
                    tmp.Add(new XElement("message",
                                         new XAttribute("name", (kv.Key.ErrorMessage.Length > 100 ? kv.Key.ErrorMessage.Substring(0, 100) + "..." : kv.Key.ErrorMessage)),
                                         new XAttribute("count", kv.Value),
                                         new XAttribute("link", string.Format(messLink, kv.Key.ErrorId.ToString("D"))),
                                         new XAttribute("type", kv.Key.ErrorType),
                                         new XAttribute("level", kv.Key.ErrorLevel))
                        );
                }
                retval.Add(app, new XElement("report",
                                             new XAttribute("name", "alert"),
                                             new XAttribute("application", app),
                                             tmp)
                    );
            }
            return retval;
        }

        #endregion

        #region Private Methods

        /// <summary>
        /// Gets the applications that contain errors and for which an email or other alert should be triggered.
        /// </summary>
        /// <param name="minutes">the nr of minutes that the database lookup should return in the database.</param>
        /// <param name="alertAnonymousErrorCount"></param>
        /// <param name="alertAuthenticatedErrorCount"></param>
        private static IList<string> GetApplicationsToAlert(int minutes, int alertAnonymousErrorCount, int alertAuthenticatedErrorCount)
        {
            var retval = new List<string>();
            var from = DateTime.Now.AddMinutes(-minutes);

            //Evaluate 'authenticated user' errors,
            var holder = service.GetApplicationErrorCount(from, true);
            foreach (var kv in holder)
                if (!retval.Contains(kv.Key) && kv.Value >= alertAuthenticatedErrorCount)
                    retval.Add(kv.Key);

            //Evaluate 'anonymous user' errors.
            holder = service.GetApplicationErrorCount(from, false);
            foreach (var kv in holder)
                if (!retval.Contains(kv.Key) && kv.Value >= alertAnonymousErrorCount)
                    retval.Add(kv.Key);

            return retval;
        }

        #endregion Private Methods
    }
}